#ifndef H_CORE_HASH_H
#define H_CORE_HASH_H

#define HASH_MAX_LEN (16384)

#include <memory>
#include <iostream>
#include "../util/util.h"

namespace QinChat{
    namespace Core{

        template <typename T>
        struct HashNode{
            T data;
            std::string key;
            HashNode<T>* next;
        };

        template <typename T, unsigned int L = HASH_MAX_LEN>
        class Hash{
            public:
                explicit Hash(unsigned int unSize = L):m_nLen(unSize){
                    Init(unSize);
                }
                
                ~Hash(void){
                    Destory();
                }

                bool Push(const std::string& strKey, const T& data){
                    HashNode<T>* pNewNode = new HashNode<T>;
                    if (nullptr == pNewNode){
                        return false;
                    }
                    pNewNode->data = data;
                    pNewNode->key = strKey;
                    pNewNode->next = nullptr;

                    ++m_unSize;

                    unsigned int key = HashKey(strKey.c_str()) % m_nLen;
                    if (nullptr == m_hashTable[key]){
                        m_hashTable[key] = pNewNode;
                        return true;
                    }

                    HashNode<T>* pHeadNode = m_hashTable[key];
                    while (nullptr != pHeadNode->next){
                         pHeadNode =  pHeadNode->next;
                    }

                    pHeadNode->next = pNewNode;
                    return true;
                }

                bool Remove(const std::string& strKey, T& data){
                    unsigned int key = HashKey(strKey.c_str()) % m_nLen;
                    
                    HashNode<T>* pHeadNode = m_hashTable[key];
					if (nullptr == pHeadNode) {
						return false;
					}

					if (0 == strKey.compare(pHeadNode->key)) {
						HashNode<T>* pNextNode = pHeadNode->next;
						data = pHeadNode->data;
						delete pHeadNode;
						m_hashTable[key] = pNextNode;
                        --m_unSize;
						return true;
					}
					else {
						HashNode<T>* pCurNode = pHeadNode;
						HashNode<T>* pNextNode = pCurNode->next;
						while (nullptr != pNextNode){
							if (0 == strKey.compare(pNextNode->key)) {
								pCurNode->next = pNextNode->next;
								
								data = pNextNode->data;
								delete pNextNode;
                                --m_unSize;
								return true;
							}
							pCurNode = pNextNode;
							pNextNode = pNextNode->next;
						}
					}

					return false;
                }

                bool Find(const std::string& strKey, T& data) const{
                    unsigned int key = HashKey(strKey.c_str()) % m_nLen;
                    HashNode<T>* pHeadNode = m_hashTable[key];
                    while (nullptr != pHeadNode){
                        if (0 == strKey.compare(pHeadNode->key)){
                            data = pHeadNode->data;
                            return true;
                        }
                        pHeadNode = pHeadNode->next;
                    }
                    return false;
                }

                unsigned int GetSize(void) const { return m_unSize; }
                
                void Clear(void){
                    for (auto i = 0; i < m_nLen; ++i){
                        delete m_hashTable[i] ;
                        m_hashTable[i] = nullptr;
                    }
                }
                

            protected:
                void Init(unsigned int unSize){
                    if (unSize >= L){
                        unSize = L;
                    }

                    m_nLen = unSize;
                    m_hashTable = new HashNode<T>*[unSize];
                    for (auto i = 0; i < m_nLen; ++i){
                        m_hashTable[i] = nullptr;
                    }
                    // memset(m_hashTable, 0, sizeof(HashNode<T>) * unSize);
                }
                
                void Destory(void){
                    Clear();

                    delete [] m_hashTable;
                }

            private:
                HashNode<T>** m_hashTable;
                unsigned int m_nLen = 0;
                unsigned int m_unSize = 0;

        };
    }
}

#endif // !  H_CORE_HASH_H